iT邦幫忙

2022 iThome 鐵人賽

DAY 15
0
自我挑戰組

30天學習flutter系列 第 15

15.flutter的狀態管理(三)

  • 分享至 

  • xImage
  •  

狀態管理:InheritedWidget

InheritedWidget是一個功能型組件,並且提供了一種在widget tree中從上到下共享數據的方法。[Intro]

當我們在一個widget中使用InheritedWidget共享了一個數據,那麼我們便可以在該widget下的任意子widget中來獲取該共享的數據。(與react中的context功能類似,通過數據從上而下來進行傳遞數據)

運作原理:

我們打開flutter\packages\flutter\lib\src\widgets\framework.dart

https://ithelp.ithome.com.tw/upload/images/20220930/20108931CwaaYEqbou.png

我們能看到InheritedWidget透過updateShouldNotify來決定是否應通知從該widget繼承的小widget要重新build。

測試

我們回到我們的flutter poject

首先,我們創建一個InheritedWidget,並命名為user_header_component.dart
https://ithelp.ithome.com.tw/upload/images/20220930/201089311nKPyYaBOT.png
user_header_component.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/src/foundation/key.dart';
import 'package:flutter/src/widgets/framework.dart';

class UserHeader extends InheritedWidget {
  final bool isLogin;

  const UserHeader({
    Key? key,
    required this.isLogin,
    required Widget child,
  }) : super(key: key, child: child);

  // 定義一個方便子樹中的widget獲取共享數據的方法
  static UserHeader? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<UserHeader>();
  }

  @override
  bool updateShouldNotify(UserHeader oldWidget) {
    return oldWidget.isLogin != isLogin;
  }
}

我們打開我們的todo\lib\ui\screens\user_screen.dart並添加在State

class _UserScreenState extends State<UserScreen> {

  ...
  
  bool _isLogin = false;

  @override
  Widget build(BuildContext context) {
    
    ...

                child:Column(
                  children: [
                    UserHeader(
                      isLogin: _isLogin,
                      child: Container(
                        child: _TestWidget(),
                      ),
                    ),
                    TextButton(
                        onPressed: () {
                          setState(() {
                            _isLogin = !_isLogin;
                          });
                        },
                        child: Text('set login'))
                  ],
                ),


    ...

}


class _TestWidget extends StatefulWidget {
  const _TestWidget({Key? key}) : super(key: key);

  @override
  State<_TestWidget> createState() => __TestWidgetState();
}

class __TestWidgetState extends State<_TestWidget> {
  final Color _color = Color(Random().nextInt(0xffffffff));

  @override
  Widget build(BuildContext context) {
    return Container(
      color: _color,
      child: Text(
        'Login: ' + UserHeader.of(context)!.isLogin.toString(),
      ),
    );
  }
}

這裡,我們能看到他會一直呼叫print,並且只對有使用該數據的Widget更新,所以__TestWidgetState裡的Container沒有被rebuild(如果有的話顏色會變)

app-test.gif

接著,我們小小修改一下_TestWidget

class __TestWidgetState extends State<_TestWidget> {
  final Color _color = Color(Random().nextInt(0xffffffff));

  @override
  Widget build(BuildContext context) {
    return Container(
      color: _color,
      // child: Text(
      //   'Login: ' + UserHeader.of(context)!.isLogin.toString(),
      // ),
      child: Text('test'),
    );
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 當build有使用到InheritedWidget,就會呼叫
    print("Dependencies change");
  }
}

app-test2.gif

我們能看到當點擊按鈕後,雖然我們的UserHeadert的data雖然發生變化。

但是現在我們的_TestWidgetState並未依賴UserHeadert,所以_TestWidgetState不會呼叫didChangeDependencies方法。此機制是在數據發生變化時只對使用該數據的Widget更新

[上述例子參考《Flutter實戰·第二版》]


今天簡單的介紹了InheritedWidget,明天會開始介紹Provider


// user_screen.dart

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter/src/foundation/key.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:todo/ui/components/user_header_component.dart';

class UserScreen extends StatefulWidget {
  const UserScreen({Key? key}) : super(key: key);

  @override
  State<UserScreen> createState() => _UserScreenState();
}

class _UserScreenState extends State<UserScreen> {
  final TrackingScrollController _trackingScrollController =
      TrackingScrollController();

  bool _isLogin = false;

  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      controller: _trackingScrollController,
      slivers: [
        SliverAppBar(
          title: Text('User Screen'),
        ),
        SliverToBoxAdapter(
          child: Container(
            padding: const EdgeInsets.all(8.0),
            child: Row(children: [
              Placeholder(
                fallbackHeight: 50,
                fallbackWidth: 50,
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Column(
                  children: [
                    UserHeader(
                      isLogin: _isLogin,
                      child: Container(
                        child: _TestWidget(),
                      ),
                    ),
                    TextButton(
                        onPressed: () {
                          setState(() {
                            _isLogin = !_isLogin;
                          });
                        },
                        child: Text('set login'))
                  ],
                ),
              ),
            ]),
          ),
        ),
      ],
    );
  }
}

class _TestWidget extends StatefulWidget {
  const _TestWidget({Key? key}) : super(key: key);

  @override
  State<_TestWidget> createState() => __TestWidgetState();
}

class __TestWidgetState extends State<_TestWidget> {
  final Color _color = Color(Random().nextInt(0xffffffff));

  @override
  Widget build(BuildContext context) {
    return Container(
      color: _color,
      child: Text(
        'Login: ' + UserHeader.of(context)!.isLogin.toString(),
      ),
    );
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 當build有使用到InheritedWidget,就會呼叫
    print("Dependencies change");
  }
}

// user_header_component.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/src/foundation/key.dart';
import 'package:flutter/src/widgets/framework.dart';

class UserHeader extends InheritedWidget {
  final bool isLogin;

  const UserHeader({
    Key? key,
    required this.isLogin,
    required Widget child,
  }) : super(key: key, child: child);

  // 定義一個方便子樹中的widget獲取共享數據的方法
  static UserHeader? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<UserHeader>();
  }

  @override
  bool updateShouldNotify(UserHeader oldWidget) {
    return oldWidget.isLogin != isLogin;
  }
}


上一篇
14.flutter的狀態管理(二)
下一篇
16.flutter的狀態管理(四)
系列文
30天學習flutter30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言